package com.stox.client.ui.javafx.chartingy.widget;

import java.util.List;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.scene.shape.Line;

import com.stox.client.ui.javafx.chartingy.model.chart.Chart;
import com.stox.client.ui.javafx.chartingy.model.plot.Data;
import com.stox.client.ui.javafx.chartingy.view.ChartingView;
import com.stox.client.ui.javafx.core.util.CSS;
import com.stox.client.ui.javafx.core.util.UI;
import com.stox.shared.model.Bar;
import com.stox.shared.model.Scrip;
import com.stox.shared.util.Constant;

public class Crosshair extends Group implements EventHandler<MouseEvent> {

	private final ChartingView chartingView;
	private final Line vertical = new Line();
	private final Line horizontal = new Line();
	private final Label valueLabel = new Label();
	private final Label dateLabel = new Label();

	public Crosshair(final ChartingView chartingView) {
		this.chartingView = chartingView;
		setManaged(false);
		setMouseTransparent(true);
		UI.style(this, CSS.crosshair);
		chartingView.getCenter().addEventFilter(MouseEvent.MOUSE_MOVED, this);
		chartingView.getCenter().addEventFilter(MouseEvent.MOUSE_DRAGGED, this);

		final Region pane = (Region) chartingView.getCenter();
		vertical.endXProperty().bindBidirectional(vertical.startXProperty());
		vertical.startYProperty().bind(pane.layoutYProperty());
		vertical.endYProperty().bind(pane.heightProperty().subtract(chartingView.getRangeSlider().heightProperty()));
		dateLabel.layoutXProperty().bind(vertical.startXProperty().subtract(dateLabel.widthProperty().divide(2)));
		dateLabel.setVisible(false);
		chartingView.getPrimaryChart().getDateAxis().layoutYProperty().addListener(new ChangeListener<Number>() {
			@Override
			public void changed(final ObservableValue<? extends Number> observable, final Number oldValue, final Number newValue) {
				Point2D point = chartingView.getPrimaryChart().localToScene(0, newValue.doubleValue());
				point = chartingView.getCenter().sceneToLocal(point);
				dateLabel.setLayoutY(point.getY());
			}
		});

		horizontal.endYProperty().bindBidirectional(horizontal.startYProperty());
		horizontal.startXProperty().bind(pane.layoutXProperty());
		horizontal.endXProperty().bind(pane.widthProperty().subtract(valueLabel.widthProperty()));
		valueLabel.layoutXProperty().bind(horizontal.endXProperty());
		valueLabel.layoutYProperty().bind(horizontal.startYProperty().subtract(valueLabel.heightProperty().divide(2)));
		valueLabel.setVisible(false);
		getChildren().addAll(vertical, horizontal, valueLabel, dateLabel);
	}

	@Override
	public void handle(final MouseEvent event) {
		if (!isVisible()) {
			return;
		}

		Point2D point = chartingView.getCenter().sceneToLocal(event.getSceneX(), event.getSceneY());
		vertical.setStartX(point.getX());
		horizontal.setStartY(point.getY());

		Chart<?, ?> chart = null;
		Region contentArea = chartingView.getPrimaryChart().getChartContentArea();
		point = contentArea.sceneToLocal(event.getSceneX(), event.getSceneY());
		if (contentArea.contains(point)) {
			chart = chartingView.getPrimaryChart();
		}
		if (null == chart) {
			for (final Chart<?, ?> chart1 : chartingView.getCharts()) {
				contentArea = chart1.getChartContentArea();
				point = contentArea.sceneToLocal(event.getSceneX(), event.getSceneY());
				if (contentArea.contains(point)) {
					chart = chart1;
					break;
				}
			}
		}

		if (null != chart) {
			setVisibleInternal(true);
			valueLabel.setText(chart.getValueAxis().stringValueOf(chart.getValueAxis().getValueForDisplay(point.getY())));
			final int index = chart.primaryChart().getDateAxis().getIndexForDisplay(point.getX());
			final List<Data<Scrip, Bar>> datas = chart.primaryPlot().getData();
			if ((index >= 0) && (index < datas.size())) {
				final Data<Scrip, Bar> data = datas.get(index);
				if (null != data) {
					dateLabel.setText(Constant.dateFormat.format(data.getExtraValue().getDate()));
				}
			}
		} else {
			setVisibleInternal(false);
		}
	}

	private void setVisibleInternal(final boolean value) {
		horizontal.setVisible(value);
		vertical.setVisible(value);
		dateLabel.setVisible(value);
		valueLabel.setVisible(value);
	}

}
